/*
 * Created on 2009-3-7
 *
 * TODO
 */
package jacky.lanlan.song.extension.spring.beans;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

/**
 * 从数据库里面“拉”出映射数据的Bean。
 * <p>
 * 这个Bean从数据库里面读取“属性映射表”(这种表通常是2列，一列代码，一列值，比如 类型编号-类型 )， 然后缓存到 {@code Map<String, Integer>} 中。
 * <p>
 * 通过外部注入 sql 语句和 {@code JdbcTemplate}，这个Bean自动完成上述工作。 <p style="color:red;font-weight:bolder"> 必须确保
 * sql 语句返回的 {@code ResultSet} 有名称为 key，value 的两列，并且 key 是 varchar 类型，value 是 number 类型。
 * <p>
 * 通过配置 {@code mergeMap}，可以往查询出来的Map里面添加新数据。
 * <p>
 * 通过配置 {@code excludeList}，可以删除查询出来的Map里面的数据。
 * 
 * @author Jacky.Song
 */
public class MapPuller extends JdbcDaoSupport implements FactoryBean {

	private String sql;

	private Map<Integer, String> mergeMap;

	private String excludeList;
	
	/**
	 * 设置需要混合的Map，这个Map将和查询出来的Map内容混合。
	 * <p>
	 * <i>这个Map具有更高的优先级，具有相同值的Entry会覆盖用sql查询出来的Map的Entry。</i>
	 */
	public void setMergeMap(Map<Integer, String> mergeMap) {
		this.mergeMap = mergeMap;
	}
	
	/**
	 * 设置排除列表，格式：{@code key1,key2,key3...}，这些Entry将被删除，不会出现在Map内容中。
	 */
	public void setExcludeList(String excludeList) {
		this.excludeList = excludeList;
	}
	
	/**
	 * @param sql
	 *          查询语句
	 */
	public void setSql(String sql) {
		this.sql = sql;
	}

	@Override
	public Object getObject() throws Exception {
		final Map<Integer, String> map = new HashMap<Integer, String>();

		getJdbcTemplate().query(sql, new ResultSetExtractor() {
			@Override
			public Object extractData(ResultSet rs) throws SQLException,
					DataAccessException {
				while (rs.next()) {
					map.put(rs.getInt("key"), rs.getString("value"));
				}
				return null;
			}
		});

		if (mergeMap != null) {
			map.putAll(mergeMap);// merge
		}

		if (excludeList != null) {
			for (String key : excludeList.split(",")) {
				map.remove(key);// exclude
			}
		}
		
		Map<String, Integer> result = new HashMap<String, Integer>();
		
		// revert
		for(Integer key : map.keySet()) {
			result.put(map.get(key), key);
		}
		
		return result;
	}

	@Override
	public Class getObjectType() {
		return Map.class;
	}

	@Override
	public boolean isSingleton() {
		return true;
	}

}
